#ifndef _HOOK_PROC_MANAGER_WIN_IMPL_H_
#define _HOOK_PROC_MANAGER_WIN_IMPL_H_


#include <string>
#include <exception>
#include <sstream>
#include <vector>
#include <map>

#include "boost/shared_ptr.hpp"
#include "boost/serialization/singleton.hpp"

#include <windows.h>

#include "tools/Buffer.h"

#include "tools/helpMacros.h"
#include "tools/defSingleton.h"
#include "tools/Thread.h"



namespace sd
{
namespace hp
{
namespace impl
{


using sd::tools::buffer::Buffer;
using sd::tools::buffer::PtrBuffer;
using sd::tools::buffer::StaticBuffer;
using sd::tools::buffer::DynamicBuffer;
//TODO: For greate justice for read-write operation need using atomic operation

class LibraryImpl;
struct jumper;
class OldFunctionData;
class HookImplementation;

//typedef FARPROC UntypedFunction; 
typedef HMODULE ModuleHandle;
typedef HANDLE ProcessDiscriptor;

class UntypedFunction
{
public:
    UntypedFunction( const LibraryImpl& lib, const std::string& name, FARPROC ptr ): 
        lib_( lib ), name_( name ), ptr_( ptr ){}
    const std::string getName()const{ return name_; }
    const LibraryImpl& getLibrary()const{ return lib_; }
    void* getMemory()const{ return (void*)ptr_; }
private:
	SD_TL_EMPTY_ASSIGMENT(UntypedFunction);
    const LibraryImpl& lib_;
    std::string name_;
    FARPROC ptr_;
};


class HookException : public std::exception
{
public:
	HookException( const HookImplementation&, const std::string& error );
	virtual const char * what() const{ return emsg_.c_str(); }

	const HookImplementation& getHookImpl(){ return impl_; }
private:
	std::string emsg_;
	const HookImplementation& impl_;
};



class LibraryImpl
{
public:
	LibraryImpl( const std::string& name );
	~LibraryImpl();
	UntypedFunction getFunction( const std::string& FuncName )const;
	const std::string& getName()const{ return name_; }
	
private:
	ModuleHandle lib_;
	std::string name_;
};


    
typedef void* ( __cdecl *Callback )( void* UserData, int* workedParams, 
	const void* reserv1, const void* reserv2,... );


class OldFunctionData
{
public:
    virtual ~OldFunctionData(){}
	virtual void* getPlacePointer()const=0;
	virtual DynamicBuffer createCaller( const void* place, const void* resumer )const=0;
	virtual DynamicBuffer createRepeater( const void* place )const=0;
	virtual DynamicBuffer createResumer( const void* place )const=0;
	virtual DynamicBuffer createInjector( const void* caller )const=0;
	virtual const Buffer& getOldData()const=0;
};

class HookImplementation
{
public:
	void* getData()const{ return data_; }
	bool isActive()const{ return active_;  }
	void setActive( bool active );
    const UntypedFunction& getFunction()const{ return function_; }
	Callback getCallback()const{ return callback_; }
	const jumper& getJumper(){ return *jumper_; }

	typedef void* ( __stdcall *OriginalFunctionCallerPtr )();
	OriginalFunctionCallerPtr getCallerOriginalPtr()const;

	inline void lock()
	{
		DWORD id = GetCurrentThreadId();
		
		if( lockid != id )
			while( InterlockedCompareExchange( &lockid, id, 0 ))
				Sleep(0);
		counter++;
	}
	
	inline void unlock()
	{
		assert( lockid == GetCurrentThreadId() );
		counter--;
		if(!counter)
			InterlockedExchange( &lockid, 0 );

	}


protected:	
    friend class HookedProcessImplementation;
    HookImplementation( const UntypedFunction&, Callback, void* );
	void setData( void *data ){ data_ = data; }
	void setOldData( OldFunctionData* old );
	OldFunctionData* getOldData(){ return oldData_; }
private:
	SD_TL_EMPTY_ASSIGMENT(HookImplementation);
    void* data_;
    bool active_;	
	volatile DWORD lockid;
	volatile DWORD counter;
	Callback callback_;
	OldFunctionData* oldData_;
	std::auto_ptr<jumper> jumper_;
	UntypedFunction function_;
};

typedef boost::shared_ptr<HookImplementation> HookImplementationRef;




class ProcessLocker
{
public:
	ProcessLocker(): locked_(false){}
	void lock();
	void unlock();
	bool isLocked()const{ return locked_; }
	bool checkMemoryRegionExec( const void* start_region, const void* end_region )const;

private:
	int tryLock();
	tools::thread::ThreadGroup syspended_threads;
	bool locked_;
};


class HookedProcessImplementation: public tools::defSingleton<HookedProcessImplementation>, public ProcessLocker
{
public:
  

	
	HookedProcessImplementation();
	~HookedProcessImplementation();

	
	HookImplementationRef& CreateHook( const LibraryImpl&,  const std::string& name, Callback, void* Param = 0 );
	bool checkLockedThreadsActiveCode(const void*)const;

protected:
   
    enum LockMemoryType
    {
        LM_READ =       1 << 0,
        LM_WRITE =      1 << 1,
        LM_EXECUTE =    1 << 2,
        LM_ALL =        LM_READ | LM_WRITE | LM_EXECUTE
    };
	typedef int LockMemorySet;
	
	class MemoryLocker : public PtrBuffer
	{
    public:
		MemoryLocker( HookedProcessImplementation* hpi, const Buffer& buffer, LockMemorySet lmType ):
		  impl_( hpi ), PtrBuffer( buffer ), current_( lmType )
		  {
			  oldFlags_ = impl_->setMemoryFlag( *this, toWindows ( lmType ) );
		  }
		void setFlag( LockMemoryType lmt )
		{
			if ( !( lmt & current_ ) )
			{
				current_|=lmt;
				impl_->setMemoryFlag( *this, toWindows ( current_ ) );
			}


		}
		void clearFlag( LockMemoryType lmt )
		{
			if ( lmt & current_ )
			{
				current_&= !lmt;
				impl_->setMemoryFlag( *this, toWindows ( current_ ) );
			}

		}
		
		bool isFlag( LockMemoryType lmt )
		{
			return !!( current_ && lmt );
		}
        
		~MemoryLocker()
		{
			impl_->setMemoryFlag( *this, oldFlags_ );
		}

		static DWORD toWindows( LockMemorySet lmt )
		{
			switch( lmt )
			{
			case 0: return PAGE_NOACCESS;
			case LM_READ: return PAGE_READONLY;
			case LM_WRITE: return PAGE_READWRITE;
			case LM_EXECUTE: return PAGE_EXECUTE;
			case LM_READ | LM_WRITE: return PAGE_READWRITE;
			case LM_READ | LM_EXECUTE: return PAGE_EXECUTE_READ;
			case LM_WRITE | LM_READ | LM_EXECUTE: return PAGE_EXECUTE_READWRITE;
			default: return PAGE_NOACCESS;
			};
		}
		static LockMemorySet toInner( DWORD wms )
		{
			switch( wms )
			{
			case PAGE_EXECUTE: return LM_EXECUTE;
			case PAGE_EXECUTE_READ: return LM_READ | LM_EXECUTE;
			case PAGE_EXECUTE_READWRITE: return LM_WRITE | LM_READ | LM_EXECUTE;
			case PAGE_READONLY: return LM_READ;
			case PAGE_READWRITE: return LM_READ | LM_WRITE;
			}
		}
    private:
		DWORD oldFlags_;
		LockMemorySet current_;
        HookedProcessImplementation* impl_;        
    };

    friend class MemoryLocker;
	friend class HookImplementation;

    int AddHook( const HookImplementationRef& ref );
    void SetHook( HookImplementation& ref );
    void SetHook( size_t );
    void removeHook( HookImplementation& ref );
    void removeHook( size_t );
    void writeMemory( MemoryLocker& data, const Buffer& buffer );
  
private:
	DWORD setMemoryFlag( const Buffer& buffer, DWORD );

    typedef std::vector< HookImplementationRef > Hooks;
    Hooks hooks_ ;
};





}//namespace impl
}//namespace hp
}//namespace sd


#endif//_HOOK_PROC_MANAGER_WIN_IMPL_H_
